package com.imooc.bilibili.es_service.impl;

import com.imooc.bilibili.entity.VideoInfo;
import com.imooc.bilibili.es_service.ElasticsearchService;
import com.imooc.bilibili.repository.UserRepository;
import com.imooc.bilibili.repository.VideoRepository;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service("esService")
public class ElasticsearchServiceImpl implements ElasticsearchService {

    @Autowired
    private VideoRepository videoRepository;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 添加视频数据
     *
     * @param videoInfo
     */
    @Override
    public void addVideo(VideoInfo videoInfo) {
        videoRepository.save(videoInfo);
    }

    /**
     * 根据关键词进行模糊查询
     */
    @Override
    public VideoInfo getVideo(String keyWord) {
        return videoRepository.findByTitleLike(keyWord);
    }

    /**
     * 以用户和视频为案例，实现全文你搜索
     */
    @Override
    public List<Map<String, Object>> getContents(String keyWord, Integer pageNo, Integer pageSize) throws IOException {
        //将涉及到的全文搜索内容写入字符串数组，构建全文检索的查询请求
        String[] indexes = {"videos", "users"};
        SearchRequest searchRequest = new SearchRequest(indexes);

        //分页
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(pageNo - 1);
        searchSourceBuilder.size(pageSize);

        /**
         * 构建多条件请求对象
         */
        MultiMatchQueryBuilder matchQueryBuilder = QueryBuilders.multiMatchQuery(keyWord, "videoTitle", "videoDescription", "userNick");
        searchSourceBuilder.query(matchQueryBuilder);
        searchRequest.source(searchSourceBuilder);
        //设置查询超时
        searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));

        //高亮显示
        String[] array = {"videoTitle", "videoDescription", "userNick"};
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        for (String key : array) {
            highlightBuilder.fields().add(new HighlightBuilder.Field(key));
        }
        //如果多个字段进行高亮显示，值为true，高亮就会失效
        highlightBuilder.requireFieldMatch(false);
        highlightBuilder.preTags("<span style=\"color:red\">");
        highlightBuilder.postTags("</span>");
        searchSourceBuilder.highlighter(highlightBuilder);

        //执行搜索
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        List<Map<String, Object>> list = new ArrayList<>();
        for (SearchHit hit : searchResponse.getHits()) {
            //处理高亮字段
            Map<String, HighlightField> highlightBuilderFields = hit.getHighlightFields();
            //存储处理后的内容
            Map<String, Object> sourceMap = hit.getSourceAsMap();
            for (String key : array) {
                HighlightField field = highlightBuilderFields.get(key);
                if (null != field) {
                    Text[] fragments = field.fragments();
                    String str = Arrays.toString(fragments);
                    str = str.substring(1, str.length() - 1);
                    sourceMap.put(key,str);
                }
            }
            list.add(sourceMap);
        }
        return list;
    }
}
